/*
 * TCPSocket.cpp
 *
 *  Created on: Mar 14, 2013
 *      Author: Xi
 */

#include "TCPSocket.h"
#include "DataLinkLayer.h"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h> 
#include<iostream>
#include <pthread.h>
#include <vector>
#include <fstream>
#include"DBAccess.h"
#include<sstream>

using namespace std;

int parentfd = 0; /* parent socket */
struct sockaddr_in serveraddr; /* server's addr */
pthread_t threadId = 0; /*thread for receive new connection*/
pthread_t sendThread1; /*send large message in parallel*/
pthread_t bookthread;/*process book command*/
DBAccess dba;
//temp test
int flag = 0;
vector<string> cmdqueue;/*a queue to store cmd*/
bool bookflag = true;
bool resflag = false;
string res;

TCPSocket::TCPSocket() {

}

TCPSocket::~TCPSocket() {
    // TODO Auto-generated destructor stub
}

/**
 * set up parent socket
 * @param port
 * @return 
 */
bool TCPSocket::setUp(int port) {

    cout << "inside setUp()" << endl;

    parentfd = socket(AF_INET, SOCK_STREAM, 0);

    /* this is an Internet address */
    serveraddr.sin_family = AF_INET;

    /* let the system figure out our IP address */
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);

    /* this is the port we will listen on */
    serveraddr.sin_port = htons((unsigned short) port);

    /* 
     * bind: associate the parent socket with a port 
     */
    if (bind(parentfd, (struct sockaddr *) &serveraddr, sizeof (serveraddr)) < 0) {
        cout << "bind error!!!" << endl;
        return false;
    } else {
        cout << "bind successful" << endl;
        return true;
    }

}

/**
 * Wrapper for socket send function.
 * Major use is convert received char[] to string
 * @param sockfd
 * @return 
 */
string TCPSocket::recvMsg(int sockfd) {

    /*display debug info*/
    this->utl.echoDebugInfo("Inside TCPSocket::recvMsg()");
    char msg[101];
    bzero(msg, 101);
    int res = read(sockfd, msg, 101);

    string msgStr(msg);
    return msgStr;
}

string recvMsg(int sockfd) {
    char msg[101];
    bzero(msg, 101);
    read(sockfd, msg, 101);

    string msgStr(msg);
    return msgStr;
}

/**
 * Wrapper for socket send function.
 * @param msg
 * @param sockfd
 */
void TCPSocket::sendMsg(string msg, int sockfd) {
    this->utl.echoDebugInfo("Inside TCPSocket::sendMsg()");
    char msgChar[101];
    strcpy(msgChar, msg.c_str());
    write(sockfd, msgChar, 101);
}

void sendMsg(string msg, int sockfd) {
    char msgChar[101];
    strcpy(msgChar, msg.c_str());
    write(sockfd, msgChar, 101);
}

void* sendLargeMsg(int gameid) {
    //cout << "inside thread!!!!!!!!" << endl;
	//string gameid = *((string *)gd);
	if (gameid == -1){return;}
	else{
		stringstream ss;
		ss << gameid;
		string str = ss.str();
		DataLinkLayer dl;
		dl.sendPic(str);
	}
}

void *bookticket(){
	//string bookcmd = *((string *)cmdptr);
	while(true){
		if (!cmdqueue.empty()&&!resflag){
			string bookcmd = cmdqueue.back();
			cmdqueue.pop_back();
			vector<string> cmdVector;
			Utilities u;
			char c = ' ';
			cmdVector = u.splitString(bookcmd, c, -1);
			string action = cmdVector[0];
			vector<string> params;
			params.push_back(cmdVector[1]);
			params.push_back(cmdVector[2]);
			params.push_back(cmdVector[3]);
			res = dba.query_Database(action, params);
			resflag = true;
		}
		//else resflag = false;
	}
}

string TCPSocket::parseClientCmd(string cmd, int csock) {

    string backMsg;
    /*get action type*/

    vector<std::string> cmdVector;
    Utilities u;
    char c = ' ';
    cmdVector = u.splitString(cmd, c, -1);
    string action = cmdVector[0];



    if (action == "register") {
        vector<string> params;
        params.push_back(cmdVector[1]);
        params.push_back(cmdVector[2]);
        backMsg = dba.query_Database(action, params);
    } else if (action == "login") {
        vector<string> params;
        params.push_back(cmdVector[1]);
        params.push_back(cmdVector[2]);
        backMsg = dba.query_Database(action, params);

    } else if (action == "list") {
        vector<string> params;
        if (cmdVector.size() == 3) {
            params.push_back(cmdVector[1]);
            params.push_back(cmdVector[2]);
        } else {
            params.push_back(cmdVector[1]);
        }
        backMsg = dba.query_Database(action, params);
    } else if (action == "book") {
		if (bookflag){
			pthread_create(&bookthread,0,&bookticket,NULL);
			pthread_detach(bookthread);
			bookflag = false;
		}
		cmdqueue.push_back(cmd);
        /*vector<string> params;
        params.push_back(cmdVector[1]);
        params.push_back(cmdVector[2]);
        params.push_back(cmdVector[3]);
        backMsg = dba.query_Database(action, params);

        because of the size, send out packet in parallel
        pthread_create(&sendThread1, 0, &sendLargeMsg, csock);
        pthread_detach(sendThread1);*/
		while(!resflag){
		}
		int gameid;
		if (res=="succeed"){
			gameid = atoi(cmdVector[2].c_str());
		}
		else{
			gameid = -1;
		}
		pthread_create(&sendThread1, 0, &sendLargeMsg, gameid);
		pthread_detach(sendThread1);
		backMsg = res;
		resflag = false;
    } else if (action == "pay") {
        backMsg = "Thanks for your money!";
    } else if (action == "view") {

        vector<string> params;
        params.push_back(cmdVector[1]);
        backMsg = dba.query_Database(action, params);
    } else if (action == "logoff") {
        backMsg = action;
        free(csock);
        close(csock);
    }
    return backMsg;
}



void* SocketHandler(int lp) {
    int csock = lp;
    DataLinkLayer dl;
    dl.toAppLayer(csock);
}

/**
 * Simply pawn new thread for each connection, for access to shared resources, using queue to resolve the problem
 * @return 
 */
bool TCPSocket::listenConnection() {
    cout << "inside listenConnection()" << endl;

    int clientSock; /* client socket*/
    listen(parentfd, 1000);

    int currentSock;
    while (true) {
        clientSock = (int) malloc(sizeof (int));

        int serverLen = sizeof (serveraddr);
        clientSock = accept(parentfd, (sockaddr*) & serveraddr, &serverLen);

        if (clientSock != -1) {
            cout << "inside listenConnection() accept a new connection sock: " << clientSock << endl;
            pthread_create(&threadId, 0, &SocketHandler, clientSock);
            pthread_detach(threadId);
            flag++;
        } else {
            continue;
        }
    }
}

int TCPSocket::conn(int port, char* hostname) {

    int sockfd;
    int portno = port;
    struct sockaddr_in serveraddr;
    struct hostent *server;

    /* socket: create the socket */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
        return false;

    /* gethostbyname: get the server's DNS entry */
    server = gethostbyname(hostname);
    if (server == NULL) {
        fprintf(stderr, "ERROR, no such host as %s\n", hostname);
        exit(0);
    }

    /* build the server's Internet address */
    memset(&serveraddr, 0, sizeof (serveraddr));
    bzero((char *) &serveraddr, sizeof (serveraddr));
    serveraddr.sin_family = AF_INET;
    bcopy((char *) server->h_addr, (char *) &serveraddr.sin_addr.s_addr,
            server->h_length);
    serveraddr.sin_port = htons(portno);

    /* connect: create a connection with the server */
    if (int k = connect(sockfd, (struct sockaddr *) &serveraddr, sizeof (serveraddr)) < 0) {
        return -1;
    } else {
        cout << "sockfd for client: " << sockfd << endl;
        return sockfd;
    }
}

